Xenium Data Overview

In this vignette we’ll visualize the spatially-resolved, single cell RNA-seq profiles produced by the Xenium platform. The vignette demonstrates how to load the per-transcript location data, cell x gene matrix, cell segmentation, and cell centroid information available in the Xenium outputs. The resulting Seurat object will contain the gene expression profile of each cell, the centroid and boundary of each cell, and the location of each individual detected transcript. The per-cell gene expression profiles are similar to standard single-cell RNA-seq and can be analyzed using the same tools.

Setup

This vignette requires a work-in-progress version of Seurat. First, you must install in-development versions of Seurat and SeuratObject that have Xenium support. It is recommended to update your package version at this step, as the required versions may not be fully captured in the dependencies yet.

library(remotes)
# remotes::install_github('satijalab/seurat', 'feat/imaging')
library(Seurat)
library(SeuratObject)

Mouse Brain: Load Data

This vignette is based on the mBrain_ff_partial_coronal_section pre-release dataset from 10x Genomics provided in the Mouse Brain Dataset download. These analysis steps are also compatible with the larger mBrain_ff_full_coronal_section, but will take longer to execute. First we read in the dataset and create a Seurat object. Provide the path to the data folder for a Xenium run as the input path. The RNA data is stored in the Xenium assay of the Seurat object. This step should take ~30 seconds for this partial coronal section.

path <- "mBrain_ff_partial_coronal_section/"

# Load the Xenium data
xenium.obj <- LoadXenium(path, fov = "fov")

# remove cells with 0 counts -- these cause problems for SCTransform
xenium.obj <- subset(xenium.obj, subset = nCount_Xenium > 0)

Spatial information is loaded into slots of the Seurat object, labelled by the name of “field of view” (FOV) being loaded. Initially all the data is loaded into the FOV named “fov”, by convention in this vignette. Later, we will make a cropped FOV that zooms into a region of interest. Here is a summary of the spatial information stored in the fov slot of the resulting Seurat object:

Cell Centroids: The spatial coordinates marking the centroid for each cell being profiled
# Get the center position of each centroid. There is one row per cell in this dataframe.
head(GetTissueCoordinates(xenium.obj[["fov"]][["centroids"]]))
x y cell
55.18063 7.603352 1
51.37956 24.270302 2
73.25728 8.897429 3
81.56802 18.440542 4
109.79451 29.622915 5
113.83060 17.944273 6
Cell Segmentation Boundaries: The spatial coordinates that describe the polygon segmentation of each single cell
# Get the coordinates for each segmentation vertice. Each cell will have a variable number of
# vertices describing its shape.
head(GetTissueCoordinates(xenium.obj[["fov"]][["segmentation"]]))
x y cell
42.2875 16.7875 1
42.2875 17.0000 1
63.7500 17.8500 1
63.9625 17.6375 1
69.9125 0.4250 1
69.9125 0.0000 1
Molecule positions: The spatial coordinates for each individual molecule that was detected by the assay
# Fetch molecules positions for Chrm1
head(FetchData(xenium.obj[["fov"]][["molecules"]], vars = "Gad1"))
x y molecule
41.33424 1769.226 Gad1
587.31256 1775.749 Gad1
208.73210 1787.754 Gad1
78.96893 1857.685 Gad1
61.79219 1859.725 Gad1
55.09167 1875.536 Gad1

Standard QC plots

Standard QC plots provided by Seurat are available via the Xenium assay. Here are violin plots of genes per cell (nFeature_Xenium) and transcript counts per cell (nCount_Xenium)

VlnPlot(xenium.obj, features = c("nFeature_Xenium", "nCount_Xenium"), ncol = 2, pt.size = 0)

Check that the cell and molecule information is loaded correctly by plotting the transcript locations of a few marker genes over the cell centroids. ImageDimPlot() can display cell positions and overlay the location of individual molecules via the molecules argument. The nmols argument is used to downsample the number of transcript locations displayed to control overplotting.

ImageDimPlot(xenium.obj, fov = "fov", molecules = c("Cux2", "Foxp2"), nmols = 20000, mols.cols = c("blue", 
    "green"), axes = TRUE)

Here’s a similar plot, including the pan-inhibitory neuron marker Gad1, inhibitory neuron sub-type markers Pvalb, and Sst, and astrocyte marker Gfap.

ImageDimPlot(xenium.obj, fov = "fov", molecules = c("Gad1", "Sst", "Pvalb", "Gfap"), nmols = 20000)

Here we visualize the expression level of some key layer marker genes at the per-cell level using ImageFeaturePlot() which is analogous to the FeaturePlot() function for visualizing expression on a 2D embedding. We manually adjust the max.cutoff for each gene to roughly the 90th percentile of it’s count distribution to improve contrast.

ImageFeaturePlot(xenium.obj, features = c("Cux2", "Rorb", "Bcl11b", "Foxp2"), max.cutoff = c(25, 
    35, 12, 10), size = 0.75, cols = c("white", "red"))

Cropped Images and Cell Boundaries

We can zoom in on a chosen area with the Crop() function. Once zoomed-in, we can visualize cell segmentation boundaries along with individual molecules.

cropped.coords <- Crop(xenium.obj[["fov"]], x = c(1400, 2800), y = c(2100, 2900), coords = "plot")

xenium.obj[["zoom"]] <- cropped.coords

# visualize cropped area with cell segmentations & selected molecules
DefaultBoundary(xenium.obj[["zoom"]]) <- "segmentation"
ImageDimPlot(xenium.obj, fov = "zoom", axes = TRUE, border.color = "white", border.size = 0.1, cols = "polychrome", 
    coord.fixed = FALSE, molecules = c("Gad1", "Sst", "Npy2r", "Pvalb", "Nrn1"), nmols = 10000)

Preprocessing and unsupervised analysis

We start by performing a standard unsupervised clustering analysis, essentially first treating the dataset as a scRNA-seq experiment. We use SCTransform-based normalization followed by standard dimensionality reduction and clustering.

xenium.obj <- SCTransform(xenium.obj, assay = "Xenium")
xenium.obj <- RunPCA(xenium.obj, npcs = 30, features = rownames(xenium.obj))
xenium.obj <- RunUMAP(xenium.obj, dims = 1:30)
xenium.obj <- FindNeighbors(xenium.obj, reduction = "pca", dims = 1:30)
xenium.obj <- FindClusters(xenium.obj, resolution = 0.3)

We can then visualize the results of the clustering by coloring each cell according to its cluster either in UMAP space with DimPlot() or overlaid on the image with ImageDimPlot().

DimPlot(xenium.obj, reduction = "umap")

We can visualize the expression level of the markers we looked at earlier on the UMAP coordinates.

FeaturePlot(xenium.obj, features = c("Cux2", "Bcl11b", "Foxp2", "Gad1", "Sst", "Gfap"))

We can now use ImageDimPlot() to color the cell positions colored by the cluster labels determined in the previous step.

ImageDimPlot(xenium.obj, cols = "polychrome", axes = TRUE, size = 0.75)

This vignette and the analysis methods for in-situ data are a work in progress. Please contact if you have questions or requests for additional examples. If you have any issues with the code working, please report the results of sessionInfo().

Session Info
sessionInfo()
## R version 4.0.3 (2020-10-10)
## Platform: x86_64-conda-linux-gnu (64-bit)
## Running under: Amazon Linux 2
## 
## Matrix products: default
## BLAS/LAPACK: /mnt/opt/R/R-4.0.3-conda/env/lib/libopenblasp-r0.3.12.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] sp_1.4-4           SeuratObject_4.1.1 Seurat_4.1.0.9007  remotes_2.2.0     
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.15            colorspace_2.0-3      deldir_1.0-6         
##   [4] ggridges_0.5.2        rstudioapi_0.11       spatstat.data_2.2-0  
##   [7] farver_2.0.3          leiden_0.3.3          listenv_0.8.0        
##  [10] bit64_4.0.5           ggrepel_0.8.2         fansi_1.0.3          
##  [13] R.methodsS3_1.8.1     codetools_0.2-16      splines_4.0.3        
##  [16] cachem_1.0.6          knitr_1.40            polyclip_1.10-4      
##  [19] jsonlite_1.8.4        ica_1.0-2             cluster_2.1.0        
##  [22] R.oo_1.24.0           png_0.1-7             rgeos_0.5-9          
##  [25] uwot_0.1.14           shiny_1.5.0           sctransform_0.3.4    
##  [28] spatstat.sparse_2.1-1 compiler_4.0.3        httr_1.4.2           
##  [31] assertthat_0.2.1      Matrix_1.5-1          fastmap_1.1.0        
##  [34] lazyeval_0.2.2        cli_3.4.1             later_1.3.0          
##  [37] formatR_1.7           htmltools_0.5.3       tools_4.0.3          
##  [40] igraph_1.2.6          gtable_0.3.1          glue_1.6.2           
##  [43] RANN_2.6.1            reshape2_1.4.4        dplyr_1.0.10         
##  [46] Rcpp_1.0.9            scattermore_0.8       jquerylib_0.1.4      
##  [49] vctrs_0.5.0           nlme_3.1-150          progressr_0.11.0     
##  [52] lmtest_0.9-38         spatstat.random_2.2-0 xfun_0.34            
##  [55] stringr_1.4.0         globals_0.16.2        mime_0.9             
##  [58] miniUI_0.1.1.1        lifecycle_1.0.3       irlba_2.3.3          
##  [61] goftest_1.2-2         future_1.19.1         MASS_7.3-53          
##  [64] zoo_1.8-8             scales_1.2.1          spatstat.core_2.4-4  
##  [67] promises_1.1.1        spatstat.utils_3.0-1  parallel_4.0.3       
##  [70] RColorBrewer_1.1-2    yaml_2.3.6            reticulate_1.18      
##  [73] pbapply_1.4-3         gridExtra_2.3         ggplot2_3.3.6        
##  [76] sass_0.4.2            rpart_4.1-15          stringi_1.7.8        
##  [79] highr_0.8             rlang_1.0.6           pkgconfig_2.0.3      
##  [82] matrixStats_0.57.0    evaluate_0.17         lattice_0.20-41      
##  [85] tensor_1.5            ROCR_1.0-11           purrr_0.3.5          
##  [88] labeling_0.4.2        patchwork_1.0.1.9000  htmlwidgets_1.5.2    
##  [91] bit_4.0.4             cowplot_1.1.0         tidyselect_1.2.0     
##  [94] RcppAnnoy_0.0.19      plyr_1.8.8            magrittr_2.0.3       
##  [97] R6_2.5.1              generics_0.1.3        DBI_1.1.0            
## [100] mgcv_1.8-33           pillar_1.8.1          fitdistrplus_1.1-1   
## [103] abind_1.4-5           survival_3.2-7        tibble_3.1.8         
## [106] future.apply_1.6.0    crayon_1.3.4          KernSmooth_2.23-17   
## [109] utf8_1.2.2            spatstat.geom_2.4-0   plotly_4.9.2.1       
## [112] rmarkdown_2.17        grid_4.0.3            data.table_1.14.2    
## [115] blob_1.2.1            digest_0.6.31         xtable_1.8-4         
## [118] tidyr_1.1.2           httpuv_1.5.4          R.utils_2.10.1       
## [121] munsell_0.5.0         viridisLite_0.3.0     bslib_0.4.0